package com.suray.wcs.service.core.util;

import com.google.common.collect.Sets;
import com.suray.basic.wcs.utils.Floor;
import com.suray.basic.wcs.utils.LockFloor;
import com.suray.wcs.service.pojo.Rgv;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @author Shaozn
 * 小车执行过程中锁定层
 */
public class LockFloorManager {
    private static volatile Set<LockFloor> lockedFloors;
    private static Logger log = LoggerFactory.getLogger(LockFloorManager.class);

    static {
        lockedFloors = Sets.newConcurrentHashSet();
    }

    /**
     * 获取当前被锁定的节点对象
     *
     * @return LockFloor对象
     */
    public static Set<LockFloor> getLockedFloors() {
        return lockedFloors;
    }

    /**
     * 直接解锁特定的节点
     *
     * @param Floor
     */
    public static synchronized void removeLockedFloor(Floor Floor) {
        lockedFloors.remove(Floor);
    }

    /**
     * 添加进锁定节点集合 会对资源占用进行校验, 若已被自己占用则忽略, 被其他RGV占用则抛出异常
     *
     * @param FloorSet
     */
    public static synchronized void addLockedFloors(Set<Floor> FloorSet, Rgv rgv) {
        updateLockedFloors(FloorSet, true, rgv);
    }

    /**
     * 将占用资源释放 若已被占用且为自己占用则释放, 若为其他RGV占用或未被占用则忽略
     *
     * @param FloorSet
     */
    public static synchronized void removeLockedFloors(Set<Floor> FloorSet, Rgv rgv) {
        updateLockedFloors(FloorSet, false, rgv);
    }

    /**
     * 检查待检查节点集合是否存在被占用的节点资源
     *
     * @param floorSet 待检查节点集合
     * @return 被占用则返回true, 否则返回false
     */
    public static synchronized boolean checkFloorsLocked(Set<Floor> floorSet, Rgv rgv) {
        for (LockFloor lockFloor : lockedFloors) {
            for (Floor floor : floorSet) {
                if (lockFloor.equals(floor) && !lockFloor.getLockedBy().equals(rgv)) {
                    log.info("层" + floor + "被" + lockFloor.getLockedBy() + "占用");
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 检查待检查节点集合是否存在被占用的节点资源
     *
     * @param FloorSet 待检查节点集合
     * @return 被自己占用则返回null, 否则返回占用者
     */
    public static synchronized Object checkFloorsLockBy(Set<Floor> FloorSet, Object obj) {
        for (LockFloor lockFloor : lockedFloors) {
            for (Floor floor: FloorSet) {
                if (lockFloor.equals(floor) && !lockFloor.getLockedBy().equals(obj)) {
                    log.info("层" + floor + "被" + lockFloor.getLockedBy() + "占用");
                    return lockFloor.getLockedBy();
                }
            }
        }
        return null;
    }

    /**
     * 检查该层集合FloorSet是否被该对象obj锁定
     *
     * @param FloorSet 待检查层集合
     * @return 被obj占用则返回true, 否则返回false
     */
    public static synchronized boolean isLockedByObj(Set<Floor> FloorSet, Object obj) {
        boolean isLocked = false;
        for (LockFloor lockFloor : lockedFloors) {
            for (Floor floor : FloorSet) {
                if (lockFloor.equals(floor) && lockFloor.getLockedBy().equals(obj)) {
                    isLocked = true;
                    break;
                }
            }
        }
        return isLocked;
    }


    public static Set<LockFloor> getLockFloorByObj(Object obj) {
        Set<LockFloor> lockedByObj = new HashSet<LockFloor>();
        Iterator<LockFloor> iterator = lockedFloors.iterator();
        while (iterator.hasNext()) {
            LockFloor next = iterator.next();
            if (next.getLockedBy().equals(obj)) {
                lockedByObj.add(next);
            }
        }
        return lockedByObj;
    }


    /**
     * 更新资源占用的层集合
     *
     * @param FloorSet     层结合
     * @param addOrRemove 添加为true, 删除为false
     */
    private static synchronized void updateLockedFloors(Set<Floor> FloorSet, boolean addOrRemove, Rgv rgv) {
        List<LockFloor> lockList = new ArrayList<>();
        if (addOrRemove) {
            for (Floor floor : FloorSet) {
                boolean exist = false;
                for (LockFloor lockFloor : lockedFloors) {
                    if (lockFloor.equals(floor)) {
                        exist = true;
                         if(!lockFloor.getLockedBy().equals(rgv)) {
                            log.error("占用失败, 该层已被Rgv_" + lockFloor.getLockedBy() + "占用");
                        }
                    }
                }
                if (!exist) {
                    lockedFloors.add(new LockFloor(floor, rgv));
                    // 设置该层临时占用
//					getGridDB(Floor).setBlock(true);
                    lockList.add(new LockFloor(floor, rgv));
                }
            }
            if (!lockList.isEmpty()) {
                StringBuilder stringBuilder = new StringBuilder(rgv.toString()+"锁定以下点:");
                lockList.forEach(lockFloor -> stringBuilder.append(lockFloor.toString()));
                log.info(stringBuilder.toString());
            }
        } else {
            List<LockFloor> removeList = new ArrayList<>();
            for (Floor floor : FloorSet) {
                for (LockFloor lockFloor : lockedFloors) {
                    if (lockFloor.equals(floor) && lockFloor.getLockedBy().equals(rgv)) {
                        // 设置改层临时占用解除
                        removeList.add(lockFloor);
                    }
                }
            }
            lockedFloors.removeAll(removeList);
            if (!removeList.isEmpty()) {
                StringBuilder stringBuilder = new StringBuilder(rgv.toString()+"解锁以下点:");
                removeList.forEach(lockFloor -> stringBuilder.append(lockFloor.toString()));
                log.info(stringBuilder.toString());
            }
        }
    }

    /**
     * 删除位置资源锁(从当前线程获取Rgv对象)
     */
    public static synchronized void removeUnLocationLock(Object obj) {
        Rgv rgv = (Rgv) obj;
        if (rgv == null || rgv.getRgvDB().getLocation() == null) {
            return;
        }
        Set<Floor> locationFloor = Collections.singleton(new Floor(rgv.getRgvDB().getLocationNode().getZ()));
        if (isLockedByObj(locationFloor, rgv)) {
            Set<Floor> removeSet = new HashSet<>(16);
            for (LockFloor lFloor : lockedFloors) {
                if (!lFloor.equals(new Floor(rgv.getRgvDB().getLocationNode().getZ()))) {
                    removeSet.add(new Floor(lFloor.getZ()));
                }
            }
            log.info("解锁除" + rgv.getRgvName() + "小车当前层"+rgv.getRgvDB().getLocationNode().getZ()+"之外所有层的坐标资源");
            removeLockedFloors(removeSet, rgv);
        }
    }

    /**
     * 更新位置资源锁(从当前线程获取Rgv对象)
     */
    public static synchronized void updateLocationLock(Object obj) {
        Rgv rgv = (Rgv) obj;
        if (rgv == null || rgv.getRgvDB().getLocation() == null) {
            return;
        }
        Set<Floor> locationFloor = Collections.singleton(new Floor(rgv.getRgvDB().getLocationNode().getZ()));
        if (!isLockedByObj(locationFloor, rgv)) {
            log.info("更新" + rgv.getRgvName() + "坐标为" + rgv.getRgvDB().getLocationNode().toString());
            addLockedFloors(locationFloor, rgv);
            Set<Floor> removeSet = new HashSet<>(16);
            for (LockFloor lFloor : lockedFloors) {
                if (!lFloor.equals(new Floor(rgv.getRgvDB().getLocationNode().getZ()))) {
                    removeSet.add(new Floor(lFloor.getZ()));
                }
            }
            log.info("解锁除" + rgv.getRgvName() + "小车当前层"+rgv.getRgvDB().getLocationNode().getZ()+"之外所有层的坐标资源");
            removeLockedFloors(removeSet, rgv);
        }
    }
}
